#include <errno.h>
#include <signal.h>

#include "list.h"
#include "sched.h"
#include "asm.h"
#include "debug.h"
#include "fibthread.h"
#include "fibsignal.h"

/* Internal function prototypes */
static void clean_detached (void);

/* External function definitions */

int schedule(void)
{
	fibthread_struct *prev_fibthread, *next_fibthread;

	if (active_thread_counter == 1)
		return EAGAIN;

	if (curr_thread->attr.stack_addr && curr_thread->stack_guard != NULL && *curr_thread->stack_guard == 0xDEAD)
		raise(SIGSEGV);

	prev_fibthread = curr_thread;
	next_fibthread = pick_thread();

	ready_thread(curr_thread);
	run_thread(next_fibthread);

	curr_thread = next_fibthread;
  
	return switch_context(prev_fibthread, next_fibthread);
}

void block_thread(void)
{
	struct fibthread_struct *prev_fibthread, *next_fibthread;

	prev_fibthread = curr_thread;
	next_fibthread = pick_thread();

	/* Put the current thread into join_threads list */
	//DOUBLY_LIST_PUSH_FRONT(&join_threads[prev_fibthread->id % ALL_THREADS_BUCKET_CNT], &prev_fibthread->join_node);
	run_thread(next_fibthread);
	curr_thread = next_fibthread;
	/* Fall in sleep till your join thread comes to save you !*/
	LOG_INFO(("Falling asleep till my join thread finishes !\n"));
	switch_context(prev_fibthread, next_fibthread);
}

/* 
 *         Name:  clean_detached
 *  Description: Cleans the detached threads from system  
 */
static void clean_detached (void)
{
  singly_node *tmp_pos;
  
  LOG_INFO(("Cleaninng detached ones ...\n"));

  while(!IS_LIST_EMPTY(&detached_threads)){
    SINGLY_LIST_POP_FRONT(&detached_threads,tmp_pos);
    release_thread_resources(EXTRACT_OBJECT(fibthread_struct, detach_node, tmp_pos));
  }	
}		

fibthread_struct *pick_thread(void)
{
	int i;
	
	/* En y�ksek �ncelikli seviyeden �ndeki thread'i al */
	for (i = FIBTHREAD_REAL_SCHED_PRIO_MAX - 1; i >= 0; --i) {
		/*
		WALK_LIST_FWD(&priority_queues[i], tmp_pos)
			printf("fibthread->id: %u\n", (EXTRACT_OBJECT(fibthread_struct, queue_node, tmp_pos))->id);
		*/
		if (!IS_LIST_EMPTY(&priority_queues[i]))
			return EXTRACT_OBJECT(fibthread_struct, queue_node, DOUBLY_LIST_HEAD(&priority_queues[i]));
	}
	
	return NULL;
}

/* 
 * Thread'in �al��t�r�lmas�n� ask�ya al�rken
 * ait oldu�u listeye y�kl�yoruz
 */

void ready_thread(fibthread_struct *fibthread)
{
	fibthread->thread_errno = errno;
	DOUBLY_LIST_PUSH_BACK(&priority_queues[fibthread->attr.sched_param.sched_priority], &fibthread->queue_node);
}

/*
 * Thread'i y�klerken ait oldu�u listeden siliyoruz
 */

void run_thread(fibthread_struct *fibthread)
{
	errno = curr_thread->thread_errno;
	DOUBLY_LIST_DELETE_NODE(&fibthread->queue_node);
}

int switch_context(fibthread_struct *prev_thread, fibthread_struct *next_thread)
{
	switch_thread_signal_mask(prev_thread, next_thread);
	switch_asm(&next_thread->registers, &(next_thread->stack_addr),&prev_thread->registers, &(prev_thread->stack_addr));
	/* Before go ahead first release the fibers that were detached */
	clean_detached();

	return 0;
}

/* Internal function definitions */
